-- AdM
-- Created by chengb Sep/23/2015
-- 负责广告相关逻辑

module("AdM", package.seeall);

-- 玩家超过多长时间未付费需要显示商城广告
local TIME_FOR_UNCHARGE = 72*3600;

-- 广告是否处于准备就绪状态
local readyState = false;

-- 广告渠道
local AD_CHANEL = "InMobi";

-- 内部函数声明
local whenAdCompleted;
local whenAdReady;
local whenClickAd;
local whenDismissAdScreen;
local whenLoginOk;

-- 初始化
function init()
    EventMgr.removeAll("AdM");

    -- 注册玩家登录的回调
    EventMgr.register("AdM", event.LOGIN_OK, whenLoginOk);

    -- 注册玩家关闭广告屏幕的回调
    EventMgr.register("AdM", event.DISMISS_AD_SCREEN, whenDismissAdScreen);

    -- 注册玩家点击广告的回调
    EventMgr.register("AdM", event.CLICK_AD, whenClickAd);

    -- 注册广告准备就绪的回调
    EventMgr.register("AdM", event.AD_READY_STATE, whenAdReady);

    -- 注册广告播放完毕的回调
    EventMgr.register("AdM", event.AD_COMPLETED, whenAdCompleted);
end

-- 迷宫中抽取收看广告的奖励
function fetchDungeonTvBonus()
    if not DungeonM.isInDungeon() then
        -- 不在迷宫中？
        return {};
    end

    return FormulaM.invoke("CALC_TV_BONUS_IN_DUNGEON");
end

-- 计算商城收看广告的奖励
function calcMarketAdBonus()
    return FormulaM.invoke("CALC_MARKET_AD_BONUS");
end

-- 计算点击广告的奖励
function calcAdClickBonus()
    return FormulaM.invoke("CALC_CLICK_AD_BONUS");
end

-- 获取今日剩余收看广告次数
function getRemainAdTimes()
    local maxTimes = FormulaM.invoke("GET_DAILY_WATCH_AD_TIMES");
    local adRecord = ME.user.dbase:query("ad_record", {});
    if #table.keys(adRecord) == 0 then
        return maxTimes;
    end

    local serverTime = TimeM.getServerTime();
    if serverTime >= (adRecord.reset_time or 0) then
        -- 已经超过了重置时间
        return maxTimes;
    end

    local curTimes = adRecord["times"] or 0;

    return maxTimes - curTimes;
end

-- 预增加收看次数
function preAddPlayTimes(isRollback)
    local adRecord = ME.user.dbase:query("ad_record", {});
    local curTimes = adRecord["times"] or 0;

    isRollback = isRollback or false;

    if isRollback and curTimes > 0 then
        -- 撤销次数
        adRecord["times"] = curTimes - 1;
    else
        adRecord["times"] = curTimes + 1;
    end

    trace("AdM", "ad played times : %d", adRecord["times"]);

    ME.user.dbase:set("ad_record", adRecord);
end

-- 获取今日迷宫内广告点击次数
function getDungeonAdClickTimes()
    local adClick = ME.user.dbase:query("dungeon_ad_click", {});
    if #table.keys(adClick) == 0 then
        return 0;
    end

    local serverTime = TimeM.getServerTime();
    if serverTime >= (adClick.reset_time or 0) then
        -- 已经超过了重置时间
        return 0;
    end

    local curTimes = adClick["times"] or 0;

    return curTimes;
end

-- 是否在商城中显示广告
function isShowAdInMarket()
    -- 获取最近一次付费的时间
    local lastTime = ME.user.dbase:query("last_charge_time");
    if type(lastTime) ~= "number" or lastTime == 0 then
        -- 如果从未充值过，则获取第一次登录的时间
        lastTime = ME.user.dbase:query("first_login_time", 0);
    end

    local serverTime = TimeM.getServerTime();
    if serverTime - lastTime < TIME_FOR_UNCHARGE then
        -- 最近刚付费过，不显示广告
        return false;
    end

    if not MarketM.isMarketFuncOpen() then
        -- 商城功能尚未开放
        return false;
    end

    -- 获取今日剩余收看广告次数
    local remainTimes = getRemainAdTimes();
    return remainTimes > 0;
end

-- 设置电视机格子的位置
function setTvPos(pos)
    _G["tv_pos"] = pos;
end

-- 获取电视机格子的位置
function getTvPos()
    return _G["tv_pos"];
end

-- 触发广告奖励
function triggerAdBonus(bonusType)
    if type(bonusType) ~= "number" then
        return;
    end

    trace("AdM", "触发电视机广告奖励: bonusType = " .. bonusType);
    _G["ad_bonus_type"] = _G["ad_bonus_type"] or AD_BONUS_TYPE_NONE;
    _G["ad_bonus_type"]  = bit.bor(_G["ad_bonus_type"], bonusType);
end

-- 执行迷宫电视机奖励
function doTvBonus(pos, bonusType)
    assert(type(pos) == "number", "pos必须为整型");
    assert(type(bonusType) == "number", "bonusType必须为整型");

    local grid = DungeonM.getGridByPos(pos);
    if not grid then
        trace("AdM", "格子(%d)不存在。", pos);
        return false;
    end

    if grid.type ~= GRID_TYPE_OLD_TV then
        trace("AdM", "格子(%d)不是老式电视机。", pos);
        return false;
    end

    if grid.state == GRID_STATE_DISABLE then
        trace("AdM", "格子(%d)已经失效");
        return false;
    end

    -- 标记格子失效
    setTvPos(nil);
    grid:changeState(GRID_STATE_DISABLE);

    -- 注：由于客户端和服务器的统计存在时间差，因此广告计数统一交由服务器处理。

    local bonusList = {};

    if bit.band(AD_BONUS_TYPE_WATCH, bonusType) == AD_BONUS_TYPE_WATCH then
        -- 触发了收看广告奖励
        local bonus = grid.bonus;
        BonusM.doBonus(bonus, "tv_bonus");

        table.insert(bonusList, bonus);

        -- 抛出事件
        EventMgr.fire(event.WATCH_TV_BONUS, { ["pos"] = pos, ["bonus"] = bonus,});
    end

    if bit.band(AD_BONUS_TYPE_CLICK, bonusType) == AD_BONUS_TYPE_CLICK then
        -- 触发了点击广告奖励
        local bonus = calcAdClickBonus();
        BonusM.doBonus(bonus, "click_ad_bonus");

        table.insert(bonusList, bonus);
    end

    -- 抛出通用建筑奖励事件，用于播放奖励特效
    EventMgr.fire(event.COMM_BUILDING_BONUS, {
        ["pos"]   = pos,
        ["bonus"] = { ["bonus"] = bonusList},
    });
    return true;
end

-- 结算广告奖励
function dealAdBonus()
    -- 取出奖励类型
    local bonusType = _G["ad_bonus_type"];
    if type(bonusType) ~= "number" then
        bonusType = AD_BONUS_TYPE_NONE;
    end

    trace("AdM", "开始结算广告奖励：bonusType = " .. bonusType);

    _G["ad_bonus_type"] = nil;

    -- 尝试获取电视机的位置
    local tvPos = getTvPos();
    if type(tvPos) == "number" then
        -- 处理迷宫内广告奖励
        DungeonActionM.go("watch_tv", tvPos, bonusType);
        return;
    end

    -- 处理商城广告奖励

    local bonusList = {};

    if bit.band(AD_BONUS_TYPE_WATCH, bonusType) == AD_BONUS_TYPE_WATCH then
        -- 触发了收看广告奖励
        local bonus = calcMarketAdBonus();

        table.insert(bonusList, bonus);
    end

    if bit.band(AD_BONUS_TYPE_CLICK, bonusType) == AD_BONUS_TYPE_CLICK then
        -- 触发了点击广告奖励
        local bonus = calcAdClickBonus();

        table.insert(bonusList, bonus);
    end

    if #bonusList > 0 then
        -- 抛出奖励事件，告知玩家领取
        EventMgr.fire(event.AD_BONUS_DONE, {["bonusList"] = bonusList, ["bonusType"] = bonusType,})
    else
        -- 撤销收看次数（没有看完，不算次数）
        preAddPlayTimes(true);
    end
end

-- 广告是否准备就绪
function isAdReady()
    return readyState == true;
end

-- 玩家关闭广告屏幕的回调
function whenDismissAdScreen()
    -- 结算广告奖励
    dealAdBonus();

    readyState = false;
end

-- 玩家点击广告的回调
function whenClickAd()
    -- 触发点击广告奖励
    triggerAdBonus(AD_BONUS_TYPE_CLICK);
end

-- 广告播放完毕的回调
function whenAdCompleted()
    -- 触发收看广告奖励
    triggerAdBonus(AD_BONUS_TYPE_WATCH);
end

-- 广告准备就绪的回调
function whenAdReady(args)
    readyState = args.readyState;
end

-- 玩家登录的回调
function whenLoginOk()
    if not isOverseaPackage() then
        -- 不是海外的版本，不处理
        return;
    end

    if not isShowAdInMarket() then
        -- 如果无需显示广告，不处理
        return;
    end

    -- 请求加载广告
    requestLoadAd();
end




